/* omega copyright (c) 1987,1988,1989 by Laurence Raphael Brothers */
/* save.c */

#ifndef MSDOS
#include <unistd.h>
#include <stdlib.h>
#endif

#include "glob.h"

/*Various functions for doing game saves and restores */
/*The game remembers various player information, the city level,
the country level, and the last or current dungeon level */

#if defined(MSDOS) || defined(AMIGA)
void do_compression(int, char *);
#endif

/**************** SAVE FUNCTIONS */

/* Checks to see if save file already exists.
   Checks to see if save file can be opened for write.
   The player, the city level, and the current dungeon level are saved.
*/

int save_game(compress,savestr)
int compress;
char *savestr;
{
  FILE *fd;
  int slashpos;
#ifdef SAVE_LEVELS
  int tmpdepth;
#endif
  int i,writeok=TRUE;
  plv current, save;
  char temp[200];

#ifndef MSDOS
  if (access(savestr, R_OK) == 0)
    if (access(savestr, W_OK) == 0)
    {
	mprint(" Overwrite old file?");
	writeok = (ynq() == 'y');
    }
    else
    {
	mprint(" File already exists.");
	writeok = FALSE;
    }
  else
  {
    for (slashpos = strlen(savestr); slashpos > 0 && savestr[slashpos] != '/';
	slashpos--)
	;
    if (slashpos > 0)
    {
	savestr[slashpos] = '\0';
	if (access(savestr, W_OK) == -1)
	{
	    mprint(" Unable to save to that directory.");
	    writeok = FALSE;
	}
	savestr[slashpos] = '/';
    }
  }
#endif
  change_to_user_perms();
  if (writeok) {
    fd = fopen(savestr,"wb");
    if (fd == NULL) {
      writeok = FALSE;
      mprint(" Error opening file.");
    }
  }
  if (! writeok)
  {
    morewait();
    print2("Save aborted.");
  }
  else {

    print1("Saving Game....");

    /* write the version number */
    i = VERSION;
    fwrite((char *)&i,sizeof(int),1,fd);
    /* write game id to save file */

    writeok &= save_player(fd);
    writeok &= save_country(fd);
#ifdef SAVE_LEVELS
    tmpdepth = Level->depth;
    City = msdos_changelevel(Level,E_CITY,0);
#endif
    writeok &= save_level(fd,City);
    
    if (Current_Environment == E_CITY || Current_Environment == E_COUNTRYSIDE)
      save = Dungeon;
    else if (Current_Environment == Current_Dungeon)
      save = Dungeon;
    else
      save = Level;
    for (i = 0, current = save; current; current = current->next, i++)
      ;
    if (!fwrite((char *)&i,sizeof(int),1,fd))
      writeok = FALSE;
#ifdef SAVE_LEVELS
    Level = msdos_changelevel(NULL,Current_Environment,tmpdepth);
#endif
    for (current = save; current; current = current->next)
      if (current != Level)
	writeok &= save_level(fd,current);
    if (save)
      writeok &= save_level(fd,Level);	/* put current level last */
    fclose(fd);
    if (writeok)
	print1("Game Saved.");
    else
	print1("Something didn't work... save aborted.");
#ifdef COMPRESS_SAVE_FILES
    if (writeok && compress) {
      print2("Compressing Save File....");
# if defined(MSDOS) || defined(AMIGA)
      do_compression(0, savestr);
      strcpy(temp, savestr);
      strcat(temp, "Z");
      rename(temp, savestr);
# else
      strcpy(temp,COMPRESSOR);
      strcat(temp," ");
      strcat(temp,savestr);
      system(temp);
      sprintf(temp, "%s.%s", savestr, COMPRESS_EXT);
      unlink(savestr);
      link(temp, savestr);
      unlink(temp);	/* renames, but sys-V doesn't have rename()... */
# endif
    }
#endif
    morewait();
    clearmsg();
  }
  change_to_game_perms();
  return(writeok);
}




/* saves game on SIGHUP */
/* no longer tries to compress, which hangs */
void signalsave()
{
  change_to_user_perms();
  save_game(FALSE, "Omega.Sav");
#ifdef COMPRESS_SAVE_FILES
  print1("Signal - Saving uncompressed file 'Omega.Sav'.");
  print2("You can compress it yourself, if you like.");
#else
  print1("Signal - Saving file 'Omega.Sav'.");
#endif
  morewait();
  endgraf();
  exit(0);
}


/* also saves some globals like Level->depth... */

int save_player(fd)
FILE *fd;
{
  int i;
  int ok = 1;

  /* Save random global state information */

  Player.click = (Tick + 1)%60;
  ok &= (fwrite((char *)&Player,sizeof(Player),1,fd) > 0);
  ok &= (fprintf(fd,"%s\n",Password) >= 0);
  ok &= (fprintf(fd,"%s\n",Player.name) >= 0);
  ok &= (fwrite((char *)CitySiteList,sizeof(CitySiteList),1,fd) > 0);
  ok &= (fwrite((char *)&GameStatus,sizeof(long),1,fd) > 0);
  ok &= (fwrite((char *)&Current_Environment,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Last_Environment,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Current_Dungeon,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Villagenum,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Verbosity,sizeof(char),1,fd) > 0);
  ok &= (fwrite((char *)&Time,sizeof(long),1,fd) > 0);
  ok &= (fwrite((char *)&Tick,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Searchnum,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Behavior,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Phase,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Date,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Spellsleft,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&SymbolUseHour,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&ViewHour,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&HelmHour,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Constriction,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Blessing,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&LastDay,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&RitualHour,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Lawstone,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Chaostone,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Mindstone,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Arena_Opponent,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Imprisonment,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Gymcredit,sizeof(long),1,fd) > 0);
  ok &= (fwrite((char *)&Balance,sizeof(long),1,fd) > 0);
  ok &= (fwrite((char *)&StarGemUse,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&HiMagicUse,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&HiMagic,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&FixedPoints,sizeof(long),1,fd) > 0);
  ok &= (fwrite((char *)&LastCountryLocX,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&LastCountryLocY,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&LastTownLocX,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&LastTownLocY,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&Pawndate,sizeof(int),1,fd) > 0);

  ok &= (fwrite((char *)Spells,sizeof(Spells),1,fd) > 0);

  ok &= (fwrite((char *)&Command_Duration,sizeof(Command_Duration),1,fd) > 0);
  ok &= (fwrite((char *)&Precipitation,sizeof(Precipitation),1,fd) > 0);
  ok &= (fwrite((char *)&Lunarity,sizeof(Lunarity),1,fd) > 0);
  ok &= (fwrite((char *)&ZapHour,sizeof(ZapHour),1,fd) > 0);
  ok &= (fwrite((char *)&RitualRoom,sizeof(RitualRoom),1,fd) > 0);

  /* stuff which used to be statics */
  ok &= (fwrite((char *)&twiddle,sizeof(twiddle),1,fd) > 0);
  ok &= (fwrite((char *)&saved,sizeof(saved),1,fd) > 0);
  ok &= (fwrite((char *)&onewithchaos,sizeof(onewithchaos),1,fd) > 0);
  ok &= (fwrite((char *)&club_hinthour,sizeof(club_hinthour),1,fd) > 0);
  ok &= (fwrite((char *)&winnings,sizeof(winnings),1,fd) > 0);
  ok &= (fwrite((char *)&tavern_hinthour,sizeof(tavern_hinthour),1,fd) > 0);
  ok &= (fwrite((char *)scroll_ids,sizeof(scroll_ids),1,fd) > 0);
  ok &= (fwrite((char *)potion_ids,sizeof(potion_ids),1,fd) > 0);
  ok &= (fwrite((char *)stick_ids,sizeof(stick_ids),1,fd) > 0);
  ok &= (fwrite((char *)ring_ids,sizeof(ring_ids),1,fd) > 0);
  ok &= (fwrite((char *)cloak_ids,sizeof(cloak_ids),1,fd) > 0);
  ok &= (fwrite((char *)boot_ids,sizeof(boot_ids),1,fd) > 0);
  ok &= (fwrite((char *)deepest,sizeof(int),E_MAX + 1,fd) > 0);
  ok &= (fwrite((char *)level_seed,sizeof(int),E_MAX + 1,fd) > 0);

  /* Save player possessions */

  if (Player.possessions[O_READY_HAND] == Player.possessions[O_WEAPON_HAND])
    Player.possessions[O_READY_HAND] = NULL;
  for(i=0;i<MAXITEMS;i++) ok &= save_item(fd,Player.possessions[i]);
  for(i=0;i<MAXPACK;i++) ok &= save_item(fd,Player.pack[i]);
  for(i=0;i<PAWNITEMS;i++) ok &= save_item(fd,Pawnitems[i]);

  /* Save items in condo vault */
  ok &= save_itemlist(fd,Condoitems);

  /* Save player item knowledge */
  for (i=0;i<TOTALITEMS;i++)
    ok &= (fwrite((char *)&(Objects[i].known),sizeof(Objects[i].known),1,fd) > 0);
  return ok;
}


/* Save whatever is pointed to by level */
int save_level(fd,level)
FILE *fd;
plv level;
{
  int i, j, run;
  unsigned long int mask;
  int ok = 1;

  ok &= (fwrite((char *)&level->depth,sizeof(char),1,fd) > 0);
  ok &= (fwrite((char *)&level->numrooms,sizeof(char),1,fd) > 0);
  ok &= (fwrite((char *)&level->tunnelled,sizeof(char),1,fd) > 0);
  ok &= (fwrite((char *)&level->environment,sizeof(int),1,fd) > 0);
  for (j = 0; j < MAXLENGTH; j++)
    for (i = 0; i < MAXWIDTH; i++)
      if (level->site[i][j].lstatus&CHANGED) {	/* this loc has been changed */
	for (run = i + 1; run < MAXWIDTH &&	/* find how many in a row */
	  level->site[run][j].lstatus&CHANGED; run++)
	  ;
	ok &= (fwrite((char *)&i,sizeof(int),1,fd) > 0);
	ok &= (fwrite((char *)&j,sizeof(int),1,fd) > 0);
	ok &= (fwrite((char *)&run,sizeof(int),1,fd) > 0);
	for (; i < run; i++)
	  ok &= (fwrite((char *)&level->site[i][j],sizeof(struct location),1,fd) > 0);
      }
  ok &= (fwrite((char *)&i,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&j,sizeof(int),1,fd) > 0);	/* signify end */
  /* since we don't mark the 'seen' bits as CHANGED, need to save a bitmask */
  run = 8*sizeof(long int);
  mask = 0;
  for (j = 0; j < MAXLENGTH; j++)
    for (i = 0; i < MAXWIDTH; i++) {
      if (run == 0) {
	run = 8*sizeof(long int);
	ok &= (fwrite((char *)&mask,sizeof(long int),1,fd) > 0);
	mask = 0;
      }
      mask >>= 1;
      if (level->site[i][j].lstatus&SEEN)
	mask |= (1<<(sizeof(long int)*8 - 1));
      run--;
    }
  if (run < 8*sizeof(long int))
    ok &= (fwrite((char *)&mask,sizeof(long int),1,fd) > 0);
  ok &= save_monsters(fd,level->mlist);
  for(i=0;i<MAXWIDTH;i++)
    for(j=0;j<MAXLENGTH;j++)
      if (level->site[i][j].things) {
	ok &= (fwrite((char *)&i,sizeof(int),1,fd) > 0);
	ok &= (fwrite((char *)&j,sizeof(int),1,fd) > 0);
	ok &= save_itemlist(fd,level->site[i][j].things);
      }
  ok &= (fwrite((char *)&i,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&j,sizeof(int),1,fd) > 0);	/* signify end */
  return ok;
}


int save_monsters(fd,ml)
FILE *fd;
pml ml;
{
  pml tml;
  int nummonsters=0;
  int ok = 1;
  unsigned char type;

  /* First count monsters */
  for(tml=ml;tml!=NULL;tml=tml->next) 
    if (tml->m->hp > 0) nummonsters++;

  ok &= (fwrite((char *)&nummonsters,sizeof(int),1,fd) > 0);

  /* Now save monsters */
  for(tml=ml;tml!=NULL;tml=tml->next) {
    if (tml->m->hp > 0) {
      ok &= (fwrite((char *)tml->m,sizeof(montype),1,fd) > 0);
      if (tml->m->id != HISCORE_NPC) {
	type = 0x0;
	if (strcmp(tml->m->monstring, Monsters[tml->m->id].monstring))
	  type |= 0x1;
	if (strcmp(tml->m->corpsestr, Monsters[tml->m->id].corpsestr))
	  type |= 0x2;
	ok &= (fwrite((char *)&type,sizeof(unsigned char),1,fd) > 0);
	if (type&1)
	  ok &= (fprintf(fd,"%s\n",tml->m->monstring) >= 0);
	if (type&2)
	  ok &= (fprintf(fd,"%s\n",tml->m->corpsestr) >= 0);
	ok &= save_itemlist(fd,tml->m->possessions);
      }	/* else it'll be reloaded from the hiscore file on restore */
    }
  }
  return ok;
}


/* Save o unless it's null, then save a special flag byte instead */
/* Use other values of flag byte to indicate what strings are saved */
int save_item(fd,o)
FILE *fd;
pob o;
{
  int ok = 1;
  unsigned char type;

  if (o == NULL) {
    type = 0xff;
    ok &= (fwrite((char *)&type,sizeof(type),1,fd) > 0);
  }
  else {
    type = 0;
    if (strcmp(o->objstr, Objects[o->id].objstr))
      type |= 1;
    if (strcmp(o->truename, Objects[o->id].truename))
      type |= 2;
    if (strcmp(o->cursestr, Objects[o->id].cursestr))
      type |= 4;
    ok &= (fwrite((char *)&type,sizeof(type),1,fd) > 0);
    ok &= (fwrite((char *)o,sizeof(objtype),1,fd) > 0);
    if (type&1)
      ok &= (fprintf(fd,"%s\n",o->objstr) >= 0);
    if (type&2)
      ok &= (fprintf(fd,"%s\n",o->truename) >= 0);
    if (type&4)
      ok &= (fprintf(fd,"%s\n",o->cursestr) >= 0);
  }
  return ok;
}

int save_itemlist(fd,ol)
FILE *fd;
pol ol;
{
  int numitems = 0;
  pol tol;
  int ok = 1;

  for(tol=ol;tol!=NULL;tol=tol->next) numitems++;
  ok &= (fwrite((char *)&numitems,sizeof(int),1,fd) > 0);
  for(tol=ol;tol!=NULL;tol=tol->next)
    ok &= save_item(fd,tol->thing);
  return ok;
}


int save_country(fd)
FILE *fd;
{
  int i, j;
  int ok = 1;
  int run;
  unsigned long int mask;

  for (i = 0; i < MAXWIDTH; i++)
    for (j = 0; j < MAXLENGTH; j++)
      if (c_statusp(i, j, CHANGED)) {
	ok &= (fwrite((char *)&i,sizeof(int),1,fd) > 0);
	ok &= (fwrite((char *)&j,sizeof(int),1,fd) > 0);
	ok &= (fwrite((char *)&Country[i][j],sizeof(struct terrain),1,fd) > 0);
      }
  ok &= (fwrite((char *)&i,sizeof(int),1,fd) > 0);
  ok &= (fwrite((char *)&j,sizeof(int),1,fd) > 0);
  /* since we don't mark the 'seen' bits as CHANGED, need to save a bitmask */
  run = 8*sizeof(long int);
  mask = 0;
  for (i = 0; i < MAXWIDTH; i++)
    for (j = 0; j < MAXLENGTH; j++) {
      if (run == 0) {
	run = 8*sizeof(long int);
	ok &= (fwrite((char *)&mask,sizeof(long int),1,fd) > 0);
	mask = 0;
      }
      mask >>= 1;
      if (c_statusp(i, j, SEEN))
	mask |= (1<<(sizeof(long int)*8 - 1));
      run--;
    }
  if (run < 8*sizeof(long int))
    ok &= (fwrite((char *)&mask,sizeof(long int),1,fd) > 0);
  return ok;
}




/* read player data, city level, dungeon level,
   check on validity of save file, etc.
   return TRUE if game restored, FALSE otherwise */

int restore_game(savestr)
char *savestr;
{
  int i,version; 
  char temp[200];
  FILE *fd;

#ifndef MSDOS
  if (access(savestr, F_OK|R_OK|W_OK) == -1) /* access uses real uid */
  {
    print1("Unable to access save file: ");
    nprint1(savestr);
    morewait();
    return FALSE;
  }
#endif
  change_to_user_perms();
#ifdef COMPRESS_SAVE_FILES
  fd = fopen(savestr,"rb");
  if (fd == NULL) {
    print1("Error restoring game -- aborted.");
    print2("File name was: ");
    nprint2(savestr);
    morewait();
    change_to_game_perms();
    return(FALSE);
  }
  fread((char *)&version,sizeof(int),1,fd);
  fclose(fd);
  if (VERSION != version) {
    print1("Uncompressing Save File....");
#if defined(MSDOS) || defined(AMIGA)
    strcpy(temp, savestr);
    strcat(temp, "Z");
    rename(savestr, temp);
    do_compression(1, savestr);
#else
    sprintf(temp, "%s.%s", savestr, COMPRESS_EXT);
    unlink(temp);
    link(savestr, temp);
    unlink(savestr);	/* renames, but sys-V doesn't have rename()... */
    strcpy(temp,UNCOMPRESSOR);
    strcat(temp," ");
    strcat(temp,savestr);
    system(temp);
#endif
    print2("Save file uncompressed.");
    morewait();
  }
#endif
  
  fd = fopen(savestr,"rb");

  if (fd == NULL) {
    print1("Error restoring game -- aborted.");
    print2("File name was: ");
    nprint2(savestr);
    morewait();
    change_to_game_perms();
    return(FALSE);
  }
  else {
    print1("Restoring...");

    fread((char *)&version,sizeof(int),1,fd);

    if (VERSION != version) {
      print2(" Sorry, I can't restore an outdated save file!");
      morewait();
      change_to_game_perms();
      return(FALSE);
    }
    restore_player(fd);
    restore_country(fd);
    restore_level(fd); /* the city level */
    fread((char *)&i,sizeof(int),1,fd);
    for (; i > 0; i--) {
#ifdef SAVE_LEVELS
      msdos_changelevel(Level,0,-1);
#endif
      restore_level(fd);
      if (Level->environment == Current_Dungeon) {
	Level->next = Dungeon;
	Dungeon = Level;
      }
      if (Current_Environment == E_CITY)
	Level = City;
    }
    /* this disgusting kludge because LENGTH and WIDTH are globals... */
    WIDTH = 64;
    switch (Current_Environment) {
      case E_COURT:
	LENGTH = 24; break;
      case E_ARENA: case E_ABYSS: case E_CIRCLE: case E_MANSION:
      case E_HOUSE: case E_HOVEL: case E_DLAIR: case E_STARPEAK:
      case E_MAGIC_ISLE: case E_TEMPLE: case E_VILLAGE:
	LENGTH = 16; break;
      default:
	LENGTH = 64; break;
    }
    fclose(fd);
    print3("Restoration complete.");
    ScreenOffset = -1000;	/* to force a redraw */
    setgamestatus(SKIP_MONSTERS);
    change_to_game_perms();
    return(TRUE);
  }
}

void restore_player(fd)
FILE *fd;
{
  int i;
  fread((char *)&Player,sizeof(Player),1,fd);
  filescanstring(fd,Password);
  filescanstring(fd,Player.name);
  fread((char *)CitySiteList,sizeof(CitySiteList),1,fd);
  fread((char *)&GameStatus,sizeof(long),1,fd);
  fread((char *)&Current_Environment,sizeof(int),1,fd);
  fread((char *)&Last_Environment,sizeof(int),1,fd);
  fread((char *)&Current_Dungeon,sizeof(int),1,fd);
  fread((char *)&Villagenum,sizeof(int),1,fd);
  switch(Current_Dungeon) {
    case E_ASTRAL: MaxDungeonLevels = ASTRALLEVELS; break;
    case E_SEWERS: MaxDungeonLevels = SEWERLEVELS; break;
    case E_CASTLE: MaxDungeonLevels = CASTLELEVELS; break;
    case E_CAVES: MaxDungeonLevels = CAVELEVELS; break;
    case E_VOLCANO: MaxDungeonLevels = VOLCANOLEVELS; break;
  }
  fread((char *)&Verbosity,sizeof(char),1,fd);
  fread((char *)&Time,sizeof(long),1,fd);
  fread((char *)&Tick,sizeof(int),1,fd);
  fread((char *)&Searchnum,sizeof(int),1,fd);
  fread((char *)&Behavior,sizeof(int),1,fd);
  fread((char *)&Phase,sizeof(int),1,fd);
  fread((char *)&Date,sizeof(int),1,fd);
  fread((char *)&Spellsleft,sizeof(int),1,fd);
  fread((char *)&SymbolUseHour,sizeof(int),1,fd);
  fread((char *)&ViewHour,sizeof(int),1,fd);
  fread((char *)&HelmHour,sizeof(int),1,fd);
  fread((char *)&Constriction,sizeof(int),1,fd);
  fread((char *)&Blessing,sizeof(int),1,fd);
  fread((char *)&LastDay,sizeof(int),1,fd);
  fread((char *)&RitualHour,sizeof(int),1,fd);
  fread((char *)&Lawstone,sizeof(int),1,fd);
  fread((char *)&Chaostone,sizeof(int),1,fd);
  fread((char *)&Mindstone,sizeof(int),1,fd);
  fread((char *)&Arena_Opponent,sizeof(int),1,fd);
  fread((char *)&Imprisonment,sizeof(int),1,fd);
  fread((char *)&Gymcredit,sizeof(long),1,fd);
  fread((char *)&Balance,sizeof(long),1,fd);
  fread((char *)&StarGemUse,sizeof(int),1,fd);
  fread((char *)&HiMagicUse,sizeof(int),1,fd);
  fread((char *)&HiMagic,sizeof(int),1,fd);
  fread((char *)&FixedPoints,sizeof(long),1,fd);
  fread((char *)&LastCountryLocX,sizeof(int),1,fd);
  fread((char *)&LastCountryLocY,sizeof(int),1,fd);
  fread((char *)&LastTownLocX,sizeof(int),1,fd);
  fread((char *)&LastTownLocY,sizeof(int),1,fd);
  fread((char *)&Pawndate,sizeof(int),1,fd);

  fread((char *)Spells,sizeof(Spells),1,fd);

  fread((char *)&Command_Duration,sizeof(Command_Duration),1,fd);
  fread((char *)&Precipitation,sizeof(Precipitation),1,fd);
  fread((char *)&Lunarity,sizeof(Lunarity),1,fd);
  fread((char *)&ZapHour,sizeof(ZapHour),1,fd);
  fread((char *)&RitualRoom,sizeof(RitualRoom),1,fd);

  /* stuff which used to be statics */
  fread((char *)&twiddle,sizeof(twiddle),1,fd);
  fread((char *)&saved,sizeof(saved),1,fd);
  fread((char *)&onewithchaos,sizeof(onewithchaos),1,fd);
  fread((char *)&club_hinthour,sizeof(club_hinthour),1,fd);
  fread((char *)&winnings,sizeof(winnings),1,fd);
  fread((char *)&tavern_hinthour,sizeof(tavern_hinthour),1,fd);
  fread((char *)scroll_ids,sizeof(scroll_ids),1,fd);
  fread((char *)potion_ids,sizeof(potion_ids),1,fd);
  fread((char *)stick_ids,sizeof(stick_ids),1,fd);
  fread((char *)ring_ids,sizeof(ring_ids),1,fd);
  fread((char *)cloak_ids,sizeof(cloak_ids),1,fd);
  fread((char *)boot_ids,sizeof(boot_ids),1,fd);
  fread((char *)deepest,sizeof(int),E_MAX + 1,fd);
  fread((char *)level_seed,sizeof(int),E_MAX + 1,fd);

  /* Set up the strings for the id's */
  inititem(FALSE);

  for(i=0;i<MAXITEMS;i++) 
    Player.possessions[i] = restore_item(fd);

  if (!Player.possessions[O_READY_HAND] && Player.possessions[O_WEAPON_HAND] &&
    twohandedp(Player.possessions[O_WEAPON_HAND]->id))
    Player.possessions[O_READY_HAND] = Player.possessions[O_WEAPON_HAND];

  for(i=0;i<MAXPACK;i++) 
    Player.pack[i] = restore_item(fd);
  for(i=0;i<PAWNITEMS;i++) 
    Pawnitems[i] = restore_item(fd);
  Condoitems = restore_itemlist(fd);
  for (i=0;i<TOTALITEMS;i++) 
    fread((char *)&(Objects[i].known),sizeof(Objects[i].known),1,fd);
}



/* Restore an item, the first byte tells us if it's NULL, and what strings */
/* have been saved as different from the typical */
pob restore_item(fd)
FILE *fd;
{
  char tempstr[80];
  unsigned char type;
  pob obj = NULL;

  fread((char *)&type,sizeof(type),1,fd);
  if (type != 0xff) {
    obj = ((pob) checkmalloc(sizeof(objtype)));
    fread((char *)obj,sizeof(objtype),1,fd);
    if (type&1) {
      filescanstring(fd,tempstr);
      obj->objstr = salloc(tempstr);
    }
    else
      obj->objstr = Objects[obj->id].objstr;
    if (type&2) {
      filescanstring(fd,tempstr);
      obj->truename = salloc(tempstr);
    }
    else
      obj->truename = Objects[obj->id].truename;
    if (type&4) {
      filescanstring(fd,tempstr);
      obj->cursestr = salloc(tempstr);
    }
    else
      obj->cursestr = Objects[obj->id].cursestr;
  }
  return obj;
}

pol restore_itemlist(fd)
FILE *fd;
{
  pol ol=NULL,cur=NULL,new=NULL;
  int i,numitems,firsttime=TRUE;
  fread((char *)&numitems,sizeof(int),1,fd);
  for(i=0;i<numitems;i++) {
    new = ((pol) checkmalloc(sizeof(oltype)));
    new->thing = restore_item(fd);
    new->next = NULL;
    if (firsttime==TRUE) {
      ol = cur = new;
      firsttime = FALSE;
    }
    else {
      cur->next = new;
      cur = new;
    }
  }
  return(ol);
}






void restore_level(fd)
FILE *fd;
{
  int i, j, run;
  unsigned long int mask;
  int temp_env;

  Level = (plv) checkmalloc(sizeof(levtype));
  clear_level(Level);
  fread((char *)&Level->depth,sizeof(char),1,fd);
  fread((char *)&Level->numrooms,sizeof(char),1,fd);
  fread((char *)&Level->tunnelled,sizeof(char),1,fd);
  fread((char *)&Level->environment,sizeof(int),1,fd);
  Level->generated = TRUE;
  temp_env = Current_Environment;
  Current_Environment = Level->environment;
  switch(Level->environment) {
    case E_COUNTRYSIDE:
      load_country();
      break;
    case E_CITY:
      load_city(FALSE);
      break;
    case E_VILLAGE:
      load_village(Country[LastCountryLocX][LastCountryLocY].aux, FALSE);
      break;
    case E_CAVES: 
      initrand(Current_Environment, Level->depth);
      if ((random_range(4)==0) && (Level->depth < MaxDungeonLevels))
	room_level();
      else cavern_level();
      break;
    case E_SEWERS: 
      initrand(Current_Environment, Level->depth);
      if ((random_range(4)==0) && (Level->depth < MaxDungeonLevels))
	room_level();
      else sewer_level(); 
      break;
    case E_CASTLE:
      initrand(Current_Environment, Level->depth);
      room_level();
      break;
    case E_ASTRAL:
      initrand(Current_Environment, Level->depth);
      maze_level();
      break;
    case E_VOLCANO:
      initrand(Current_Environment, Level->depth);
      switch(random_range(3)) {
      case 0: cavern_level(); break;
      case 1: room_level(); break;
      case 2: maze_level(); break;
      }
      break;
    case E_HOVEL:
    case E_MANSION:
    case E_HOUSE:
      load_house(Level->environment, FALSE);
      break;
    case E_DLAIR:
      load_dlair(gamestatusp(KILLED_DRAGONLORD), FALSE);
      break;
    case E_STARPEAK:
      load_speak(gamestatusp(KILLED_LAWBRINGER), FALSE);
      break;
    case E_MAGIC_ISLE:
      load_misle(gamestatusp(KILLED_EATER), FALSE);
      break;
    case E_TEMPLE:
      load_temple(Country[LastCountryLocX][LastCountryLocY].aux, FALSE);
      break;
    case E_CIRCLE:
      load_circle(FALSE);
      break;
    case E_COURT:
      load_court(FALSE);
      break;
    default: print3("This dungeon not implemented!"); break;
  }
  if (Level->depth > 0) {	/* dungeon... */
    install_traps();
    install_specials();
    make_stairs(-1);
    make_stairs(-1);
    initrand(-2, 0);
  }
  Current_Environment = temp_env;
  fread((char *)&i,sizeof(int),1,fd);
  fread((char *)&j,sizeof(int),1,fd);
  while (j < MAXLENGTH && i < MAXWIDTH) {
    fread((char *)&run,sizeof(int),1,fd);
    for (; i < run; i++) {
      fread((char *)&Level->site[i][j],sizeof(struct location),1,fd);
      Level->site[i][j].creature = NULL;
      Level->site[i][j].things = NULL;
    }
    fread((char *)&i,sizeof(int),1,fd);
    fread((char *)&j,sizeof(int),1,fd);
  }
  run = 0;
  for (j = 0; j < MAXLENGTH; j++)
    for (i = 0; i < MAXWIDTH; i++) {
      if (run == 0) {
	run = 8*sizeof(long int);
	fread((char *)&mask,sizeof(long int),1,fd);
      }
      if (mask&1)
	lset(i, j, SEEN);
      mask >>= 1;
      run--;
    }
  restore_monsters(fd,Level);
  fread((char *)&i,sizeof(int),1,fd);
  fread((char *)&j,sizeof(int),1,fd);
  while (j < MAXLENGTH && i < MAXWIDTH) {
    Level->site[i][j].things = restore_itemlist(fd);
    fread((char *)&i,sizeof(int),1,fd);
    fread((char *)&j,sizeof(int),1,fd);
  }
}



void restore_monsters(fd,level)
FILE *fd;
plv level;
{
  pml ml=NULL;
  int i,nummonsters;
  char tempstr[80];
  int temp_x, temp_y;
  unsigned char type;

  level->mlist = NULL;

  fread((char *)&nummonsters,sizeof(int),1,fd);
  
  for(i=0;i<nummonsters;i++) {
    ml = ((pml) checkmalloc(sizeof(mltype)));
    ml->m = ((pmt) checkmalloc(sizeof(montype)));
    ml->next = NULL;
    fread((char *)ml->m,sizeof(montype),1,fd);
    if (ml->m->id == HISCORE_NPC) {
      temp_x = ml->m->x;
      temp_y = ml->m->y;
      make_hiscore_npc(ml->m, ml->m->aux2);
      ml->m->x = temp_x;
      ml->m->y = temp_y;
    }
    else {
      fread((char *)&type,sizeof(unsigned char),1,fd);
      if (type&1) {
	filescanstring(fd,tempstr);
	ml->m->monstring = salloc(tempstr);
      }
      else
	ml->m->monstring = Monsters[ml->m->id].monstring;
      if (type&2) {
	filescanstring(fd,tempstr);
	ml->m->corpsestr = salloc(tempstr);
      }
      else
	ml->m->corpsestr = Monsters[ml->m->id].corpsestr;
      ml->m->possessions = restore_itemlist(fd);
      ml->m->meleestr = Monsters[ml->m->id].meleestr;
    }
    level->site[ml->m->x][ml->m->y].creature = ml->m;
    ml->next = level->mlist;
    level->mlist = ml;
  }
}



void restore_country(fd)
FILE *fd;
{
  int i, j;
  int run;
  unsigned long int mask;

  load_country();
  fread((char *)&i,sizeof(int),1,fd);
  fread((char *)&j,sizeof(int),1,fd);
  while (i < MAXWIDTH && j < MAXLENGTH) {
    fread((char *)&Country[i][j],sizeof(struct terrain),1,fd);
    fread((char *)&i,sizeof(int),1,fd);
    fread((char *)&j,sizeof(int),1,fd);
  }
  run = 0;
  for (i = 0; i < MAXWIDTH; i++)
    for (j = 0; j < MAXLENGTH; j++) {
      if (run == 0) {
	run = 8*sizeof(long int);
	fread((char *)&mask,sizeof(long int),1,fd);
      }
      if (mask&1)
	c_set(i, j, SEEN);
      mask >>= 1;
      run--;
    }
}
